package lab5p2;
import java.util.Random;

/**
 * <p>Title: Driver</p>
 * <p>Description: CS 235 Assignment #1</p>
 * 
 * This class is used to test operation of the Rank, Suit, Hand, 
 * and PlayingCard classes.  It creates a deck of the standard 
 * 52 playing cards, and randomly but evenly distributes them to
 * three different hands.  The contents of the hands are printed, 
 * and then a random card is removed from one hand and placed in 
 * another for each hand.  The resulting hands after the exchange 
 * are printed, and the program is finished.  This class has three 
 * methods: main, which is public and responsible for executing 
 * the program, and two private methods, emptyOrNot and compress.  
 * These two private methods aid in the operation of the program.  
 * The emptyOrNot method checks the deck to see if there are still
 * cards to be distributed, and the compress method organizes the 
 * deck after a card has been removed.   
 * 
 * @author Nick Brown
 * @email nicholas.brown@my.uwrf.edu
 * @date February 25th 2013
 */

public class Driver 
{
	public static void main(String[] args) 
	{
		// Creating an array of type PlayingCard, with size 52, to hold the deck. 
		PlayingCard[] deck = new PlayingCard[Hand.CARDS_IN_A_DECK];
		
		/* This is the for loop used to populate the deck with the 52 standard 
		 * playing cards.  We take advantage of the fact that enumerated types
		 * have a method called values that returns the enumerated types of a
		 * class in array form.  We also employ modular arithmetic to populate
		 * the deck in a highly efficient fashion.  Obviously, the results of 
		 * this loop were printed and checked for completeness before opting
		 * to use this approach to populating the deck.  Nested for loops could 
		 * have been used as well to generate the cards in a more orderly 
		 * fashion, but this way is sufficient and succinct.            
		 */
		for (int i = 0; i < deck.length; i++)
		{
			deck[i] = new PlayingCard(Rank.values()[i % 13], Suit.values()[i % 4]);
		}
		
		// Creating the separate hands, so that the  
		// cards in the deck can be added to them.
		Hand one = new Hand();
				
		/* The following loop randomly assigns playing cards to a hand.
		 *  The variable index keeps track of how many cards have been taken from the deck.
		 *  The variable randomValue is used to store the numbers generated by the random
		 *   value generator, creatively called generator, that is instantiated
		 *   below.
		 */ 
		Random generator = new Random();
		final int cards_to_take = 5;

		for (int index=0; index<cards_to_take; index++) {
			/* Using the random number generator to return a value between 0
			 * and (Hand.CARDS_IN_A_DECK - index) exclusive, which is a set 
			 * of values whose cardinality is equal to the number of cards
			 * remaining in the deck.  For example, if index = 10, then
			 * (Hand.CARDS_IN_A_DECK - index) evaluates to (52 - 10) = 42,
			 * so the generator produces a an integer value between 0 and
			 * 41, the possible outcome of which is 42 distinct values.   
			 */
			int randomValue = generator.nextInt(Hand.CARDS_IN_A_DECK - index);
			
			/* We now check the size of each hand object.  If the size is
			 * below a certain value, and a card has not been added to a
			 * hand in this loop iteration, then we take a card from the 
			 * deck and add it to that hand.  The array location of the card 
			 * to be taken from the deck and added to the hand was determined 
			 * above by the random number generator.  After the card is added
			 * we set the array location in the deck to be null, effectively
			 * removing the card from the deck, which is proper.  We also negate
			 * the value of added to reflect that a card has been added to a hand
			 * during this loop iteration, thus preventing the execution of any
			 * other if statements in the loop.  This loop is repeated until all
			 * of the cards have been evenly distributed to the three separate hands.     
			 */
			if ( one.getSize() <= 17 ) {	
				one.addCard(deck[randomValue]);
				deck[randomValue] = null;
			}
			
			/* After a card has been added to one of the hands, the deck
			 * is compressed using the compress method 
			 */
			deck = compress(deck);
		}
		
		// For loop used to print the original contents of the three hands.
		System.out.println("Original contents of hand: \n" );
		System.out.println( one + "\n");
		
		one.sort();
		System.out.println("Sorted hand: \n" );
		System.out.println( one + "\n");
	}


	/* This method is identical in operation to the compress method
	 * found in the Hand class.  The only difference between them is
	 * that this method requires a parameter of type PlayingCard[],
	 * namely the array to be compressed.  It also has a return type
	 * of PlayingCard[], which is the same array that was passed as
	 * the actual parameter.  Please refer to the documentation on
	 * the compress method in the Hand class for further details.   
	 */
	private static PlayingCard[] compress(PlayingCard[] deck)
	{
		for (int i = 0; i < Hand.CARDS_IN_A_DECK - 1; i++)
		{
			if(deck[i] == null)
			{
				deck[i] = deck[i + 1];
				deck[i + 1] = null;
			}	
		}
	
		return deck;
	}
}
